/**********************************************************************************************************************
 *
 * Project:    mGameEngine
 * File:       terrain.cpp
 * Author:     karooolek
 * Created on: Jun 23, 2009
 *
 **********************************************************************************************************************/

#include "terrain.h"

namespace mGameEngine
{
namespace Game
{

using namespace Graphics;

Terrain::Terrain() :
    SceneNode(),
    _heightField(),
    _texture(NULL),
    _tileSize(160.0f),
    _resolution(10.0f)
{
}

Terrain::Terrain(const Image *heightMap,
        const Texture *texture,
        const Vector3 &size,
        float tileSize,
        float resolution) :
    SceneNode(),
    _heightField(),
    _texture(texture),
    _tileSize(tileSize),
    _resolution(resolution)
{
    _heightField.setSize(size);
    setHeightMap(heightMap);
}

Terrain::~Terrain()
{
}

void Terrain::_generateTiles()
{
    // remove old tiles
    _drawables.clear();

    // create new tiles
    Vector3 size = _heightField.getSize();
    for(Vector2 p; p.x < size.x; p.x += _tileSize)
    {
        for(p.y = 0.0f; p.y < size.y; p.y += _tileSize)
        {
            TerrainTile *tile = new TerrainTile(this, p);
            tile->generateLowerLODMeshes(5);
            addDrawable(tile);
        }
    }
}

void Terrain::setHeightMap(const Image *heightMap)
{
    if(!heightMap)
    {
        return;
    }

    Vector2 size = _heightField.getSize();

    uint w = size.x / _resolution + 1;
    uint h = size.y / _resolution + 1;
    const uint8 *data = heightMap->getData();
    uint8 bpp = heightMap->getBitsPerPixel();
    float *heights = (float *)malloc(sizeof(float) * w * h);
    for(uint j = 0; j != h; ++j)
    {
        float y = lerp(0.0f, float(heightMap->getHeight()-1), float(j)/float(h-1));

        for(uint i = 0; i != w; ++i)
        {
            float x = lerp(0.0f, float(heightMap->getWidth()-1), float(i)/float(w-1));
            
            uint x0 = uint(floor(x)), x1 = uint(ceil(x));
            uint y0 = uint(floor(y)), y1 = uint(ceil(y));

            float h00, h01, h10, h11;
            switch(bpp)
            {
            case 8:
                h00 = *(heightMap->getDataAt(x0, y0)) / 255.0f;
                h01 = *(heightMap->getDataAt(x0, y1)) / 255.0f;
                h10 = *(heightMap->getDataAt(x1, y0)) / 255.0f;
                h11 = *(heightMap->getDataAt(x1, y1)) / 255.0f;
                break;
            case 16:
                h00 = *((uint16 *)heightMap->getDataAt(x0, y0)) / 65535.0f;
                h01 = *((uint16 *)heightMap->getDataAt(x0, y1)) / 65535.0f;
                h10 = *((uint16 *)heightMap->getDataAt(x1, y0)) / 65535.0f;
                h11 = *((uint16 *)heightMap->getDataAt(x1, y1)) / 65535.0f;
                break;
            default:
                h00 = h01 = h10 = h11 = 0.0f;
            }

            float f;
            float lerp1 = modf(x, &f);
            float lerp2 = modf(y, &f);
            float h = bilerp(h00, h10, h01, h11, lerp1, lerp2);
        
            heights[i + w * j] = h;
        }
    }
    _heightField.setHeights(w, h, heights);
    free(heights);

    // create tiles
    _generateTiles();

    // set physics actor
    if(_actor)
    {
        delete _actor;
    }
    _actor = new Physics::Body(&_heightField, 1.0f, 1.0f, 1.0f);
}

void Terrain::setTileSize(float tileSize)
{
    // no change
    if(tileSize == _tileSize)
    {
        return;
    }

    _tileSize = tileSize;
    _generateTiles();
}

}
}

